package rsj.service;

import com.rlyy.aibase.db.Dbink;
import com.rlyy.aibase.utils.base.Box;
import com.rlyy.aibase.utils.base.MapNode;
import common.exec.RsjIllegalArgueException;
import common.util.Assert;
import common.util.BoxUtil;
import common.util.DateFormat;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import rsj.dao.LogInfoDao;
import rsj.dao.UserInfoDao;
import rsj.db.PostgresqlConn;
import rsj.db.dbutils.UniqueID;
import rsj.sql.user.UserDao;
import rsj.table.UserType;
import rsj.utils.RedisUtils;

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.Connection;
import java.sql.SQLException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Period;
import java.util.Objects;

@Component
public class UserInfoService {

    @Autowired
    private UserDao userDao;
    /**
     * 记录一小时内密码连续错误过期时间
     */
    public static final int EXPIRE = 60 * 60;
    /**
     * 密码一小时内连续输入5次锁定账号
     */
    public static final int INPUT_ERRORS_THRESHOLD = 5;
    public static final String PASSWORD_ERROR = "password_error";

    private static final LogInfoDao LOG_INFO_DAO = LogInfoDao.getInstance();
    private static final UserInfoDao infoDao = UserInfoDao.getInstance();


    public MapNode getAndCheckUserInfo(MapNode data, String tid) {
        String userName = data.getValueDef("User_Uname", null);
        String userPassword = data.getValueDef("User_Password", null);
        Assert.isNotBlank(userName, "用户名为空");
        Assert.isNotBlank(userPassword, "密码为空");
        //MapNode byUserUnameAndPassword = infoDao.getByUserUnameAndPassword(conn, userName, stringToMD5(userPassword));
        //MapNode byUserUname = infoDao.getByUserUname(conn, userName);
        MapNode byUserUname = userDao.getUserByUserName(userName);

        String userId = byUserUname.getValue("User_Id");
        //判断密码为空
        Assert.isTrue(byUserUname.size() != 0, "用户名或密码错误！");
        boolean wrongPassword = !Objects.equals(byUserUname.getValue("User_Password"), stringToMD5(userPassword));
        // 密码错误
        if (wrongPassword) {
            Long numberOfInputErrors = this.lockLogin(userId);
            if (numberOfInputErrors < INPUT_ERRORS_THRESHOLD) {
                throw new RsjIllegalArgueException("密码错误，一小时内密码连续输入错误" + INPUT_ERRORS_THRESHOLD + "次账号将被锁定、剩余：" + (INPUT_ERRORS_THRESHOLD - numberOfInputErrors) + "次");
            }
            throw new RsjIllegalArgueException("账号密码连续输入错误过多账号已被锁定！");
        }
        // 密码正确 清除连续输错次数或解除账号锁定
        this.unlockAccount(userId);

        //String password = map.getValues(User.User_Password)[0];
        //System.out.println(stringToMD5(userPassword));
        //MD5加密
        //Assert.isTrue(stringToMD5(userPassword).equals(password), "用户名或密码错误！");

        return byUserUname;
    }


    /**
     * 连续输错密码五次 锁定账号
     *
     * @param userId 用户id
     * @return 输入错误次数
     */
    private Long lockLogin(String userId) {
        String passwordError = String.format(RedisUtils.redisKeyFormat, PASSWORD_ERROR, userId);
//        String numberOfInputErrors = JedisUtil.get(passwordError, tid);
        String numberOfInputErrors = RedisUtils.getString(passwordError);
        // 当第一次输入错误时 记录一小时内连续输入错误次数、
        // 当低五次输入错误 账号锁定一小时不可登录、第五此输入错误时更新记录倒计时  此倒计时是标记账号锁定的剩余时间 时间走完锁定解除
        if (Objects.isNull(numberOfInputErrors) || Integer.parseInt(numberOfInputErrors) == (INPUT_ERRORS_THRESHOLD - 1)) {
//            return JedisUtil.increase(passwordError, 1, EXPIRE, tid);
            return RedisUtils.incr(passwordError, 1, EXPIRE);
        }
//        return JedisUtil.increase(passwordError, 1, -1, tid);
        return RedisUtils.incr(passwordError, 1);
    }


    /**
     * 解除账号锁定
     *
     * @param userId 用户id
     */
    private void unlockAccount(String userId) {
        String passwordError = String.format(RedisUtils.redisKeyFormat, PASSWORD_ERROR, userId);
//        JedisUtil.del(passwordError, tid);
        RedisUtils.del(passwordError);
    }

    //private


    public MapNode findUserList(Connection conn, MapNode data) throws SQLException {

        int size = data.getValueIntDef("Page_Size", 10);
        int pcount = data.getValueIntDef("Page_Count", 1);
        MapNode responsedata = new MapNode();
        responsedata = infoDao.findUserList(conn, size, pcount);
    	/*QueryRunner queryRunner = new QueryRunner();
    	MapNode responsedata = new MapNode();
    	StringBuffer countsql = new StringBuffer();
    	countsql.append("select count(1) from "+User.Table_Name+" where "+User.User_Type +" = '"+ UserType.user_type_sys +"' and "+User.User_Status+" = '"+UserType.user_status_normal+"'");
    	Node node = queryRunner.queryLinkNode(conn, countsql.toString());
    	int count = node.valueInt();
    	responsedata.putNode("User_Count", String.valueOf(count));
    	if(count == 0){
    		responsedata.putNode("User_List", "");
    		return responsedata;

    	}

		StringBuffer sql = new StringBuffer();
		sql.append("select u."+User.User_Id+",u."+User.User_Name+",u."+User.User_Uname+",u."+User.User_Org_Id+",u."+User.User_Phone)
			.append(",u."+User.User_Wechat+",r."+Role.Role_Name+",d."+Depart.Dep_Name)
			.append(" from "+User.Table_Name+" u left join "+Role.Table_Name +" r on u."+User.User_Role_Id +"= r."+Role.Role_Id)
			.append(" left join "+ Depart.Table_Name +" d on u."+ User.User_Dep_Id + "=d."+Depart.Dep_Id)
			.append(" where "+User.User_Type +" = '"+ UserType.user_type_sys +"' and "+User.User_Status+" = '"+UserType.user_status_normal+"'")
			.append( " order by user_utime desc limit ? Offset ?");

		TableNode map = queryRunner.queryLinkTableNode(conn,sql.toString(),size,size*(pcount-1));
		responsedata.putNode("User_List", map.toJson());*/

        return responsedata;

    }

    public void insertUser(Connection conn, MapNode data) throws SQLException {

        String id = String.valueOf(UniqueID.generateId());
        String userName = data.getValue("User_Name");
        String userUname = data.getValue("User_Uname");
        String phone = data.getValue("User_Phone");
        String userWechat = data.getValue("User_Email");
        String roleId = data.getValue("Role_Id");
        String password = data.getValue("User_Password");
        String orgId = data.getValue("Org_Id");
        String depId = data.getValue("Item_Id");
        String userType = UserType.user_type_sys;//默认为系统用户

        Assert.isNotBlank(userName, "姓名不能为空");
        Assert.isNotBlank(userUname, "用户名不能为空");
        Assert.isNotBlank(phone, "手机号不能为空");
        Assert.isNotBlank(userWechat, "微信不能为空");
        Assert.isNotBlank(roleId, "角色不能为空");
        Assert.isNotBlank(password, "密码不能为空");

        infoDao.insertUser(conn, id, userUname, userName, orgId, depId, userType, stringToMD5(password), phone, userWechat, roleId);
    	/*StringBuffer findsql = new StringBuffer();
    	findsql.append("select count(1) from "+User.Table_Name+" where "+User.User_Uname+" = ?");
    	Node node = queryRunner.queryLinkNode(conn, findsql.toString(),userUname);
    	int count = node.valueInt();

    	Assert.isTrue(count == 0, "用户名已经存在，不能添加！");

    	StringBuffer sql = new StringBuffer();
    	sql.append("insert into "+User.Table_Name+" ("+User.User_Id+","+User.User_Name+","+User.User_Uname);
    	//添加系统用户有部门没有机构Id,添加机构用户，没有部门
    	if(StringUtils.isNotBlank(orgId)){
    		sql.append(","+User.User_Org_Id);
    		userType = "1";//若有组织机构 证明该用户为客户
    	}else{
    		sql.append(","+User.User_Dep_Id);
    	}
    	sql.append(","+User.User_Type+","+User.User_Password+","+User.User_Phone+","+User.User_Wechat)
    		.append(","+User.User_Role_Id+","+User.User_Status+","+User.User_Ctime+","+User.User_Utime)
    		.append(")values(?,?,?,");
    	sql.append("?,?,?,?,?,?,?,?,?)");

    	if(StringUtils.isNotBlank(orgId)){
    		queryRunner.update(conn,sql.toString(),id,userName,userUname,orgId,userType,stringToMD5(password),phone,
    				userWechat,roleId,UserType.user_type_sys,DateHelper.strToTimestamp(DateHelper.getDate(new Date())),DateHelper.strToTimestamp(DateHelper.getDate(new Date())));
    	}else{
    		queryRunner.update(conn,sql.toString(),id,userName,userUname,depId,userType,stringToMD5(password),phone,
    				userWechat,roleId,UserType.user_type_sys,DateHelper.strToTimestamp(DateHelper.getDate(new Date())),DateHelper.strToTimestamp(DateHelper.getDate(new Date())));
    	}*/

    }

    public MapNode findUserByUname(Connection conn, MapNode data) throws SQLException {

        String userUname = data.getValue("User_Uname");
    	/*StringBuffer findsql = new StringBuffer();
    	findsql.append("select u."+User.User_Id+",u."+User.User_Name+",u."+User.User_Uname+",u."+User.User_Org_Id+",u."+User.User_Phone)
    			.append(",u."+User.User_Wechat+",d."+Depart.Dep_Name+",o."+Org.Org_Id+",o."+Org.Org_Name);
    	findsql.append(" from "+User.Table_Name +" u left join "+Depart.Table_Name+" d on u."+User.User_Dep_Id+" = d."+Depart.Dep_Id)
    			.append(" left join "+Org.Table_Name+" o on u."+User.User_Org_Id+" = o."+Org.Org_Id);
    	findsql.append(" where u."+User.User_Uname+" = ?");

    	TableNode map = queryRunner.queryLinkTableNode(conn,findsql.toString(),userUname);*/
        return infoDao.findUserByUname(conn, userUname);

    }


    public void updateUser(Connection conn, MapNode data) throws SQLException {

        String userId = data.getValue("User_Id");
        String userName = data.getValue("User_Name");
        String userUname = data.getValue("User_Uname");
        String phone = data.getValue("User_Phone");
        String userWechat = data.getValue("User_Email");
        String roleId = data.getValue("Role_Id");
        String password = data.getValue("User_Password");
        String depId = data.getValue("Item_Id");

        Assert.isNotBlank(userId, "用户Id不能为空");
        Assert.isNotBlank(userUname, "用户名不能为空");
        if (StringUtils.isNoneBlank(password)) {
            password = stringToMD5(password);
        }

        infoDao.updateUser(conn, userId, userUname, userName, phone, depId, userWechat, roleId, password);
    }


    public void delUser(Connection conn, MapNode data) throws SQLException {
        String userId = data.getValue("User_Id");
        Assert.isNotBlank(userId, "用户Id不能为空");

        infoDao.delUser(conn, userId);
    }


    public void updatePassword(Connection conn, MapNode data,String userId) throws SQLException {
        String password = data.getValue("User_Password");
        String passwordnew = data.getValue("User_Passwordnew");

        Assert.isNotBlank(password, "用户原密码不能为空");
        Assert.isNotBlank(passwordnew, "用户新密码不能为空");

        infoDao.updatePassword(conn, userId, stringToMD5(password), stringToMD5(passwordnew));
    }

    public static String stringToMD5(String plainText) {
        byte[] secretBytes = null;
        try {
            secretBytes = MessageDigest.getInstance("md5").digest(
                    plainText.getBytes());
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("没有这个md5算法！");
        }
        String md5code = new BigInteger(1, secretBytes).toString(16);
        for (int i = 0; i < 32 - md5code.length(); i++) {
            md5code = "0" + md5code;
        }
        return md5code;
    }


    /**
     * 通过sso获取用户信息
     *
     * @param in sso
     * @return 用户信息
     */
    public Box getUserInfo(Box in) {
        MapNode userInfo;
        MapNode logList;
        Connection connection = PostgresqlConn.getConnection();
        try {
            MapNode data = in.getMapNode("$user");
            String id = data.getValue("User_Id");
            if (StringUtils.isBlank(id)) {
                return BoxUtil.errorBox(in, "参数不足");
            }
            userInfo = userDao.getUserByUserId(id);
            //userInfo = infoDao.getUserByUserId(connection, id);
            if (userInfo.size() == 0) {
                return BoxUtil.errorBox(in, "用户不存在");
            }
            logList = LOG_INFO_DAO.findLogList(connection, userInfo.getValue("User_Id"), 5, 1);
        } catch (Exception e) {
            e.printStackTrace();
            return BoxUtil.errorBox(in, "服务器异常");
        } finally {
            Dbink.close(connection);
        }
        userInfo.putNode("__logList__", logList.getValue("Log_List"));
        String userPasswordUtime = userInfo.getValue("User_Password_Utime");
        userInfo.putNode("pwd_valid", (StringUtils.isBlank(userPasswordUtime) || Objects.equals("null", userPasswordUtime)) ?
                "true" : this.compulsoryPasswordChange(userPasswordUtime));
        return BoxUtil.successBox(in, "成功！").putMapNode("data", userInfo);
    }

    public String compulsoryPasswordChange(String userPasswordUtime) {
        userPasswordUtime = userPasswordUtime.replace(".0", "");
        LocalDateTime passwordTime = LocalDateTime.parse(userPasswordUtime, DateFormat.DATE_TIME_FORMATTER);
        return this.updatePasswordWarning(passwordTime).toString();
    }


    public Boolean updatePasswordWarning(LocalDateTime userPwdUpdateTm) {
        Period period = Period.between(userPwdUpdateTm.toLocalDate(), LocalDate.now());
        return (period.getMonths() >= 6 || period.getYears() >= 1);
    }

}
